/**
 * 自定义快速开发脚手架
 */
import { Command } from 'commander'
import { join, sep } from 'path'
const fs = require('fs')

const program = new Command()

/**
 * program
 *  .options('-cq', --cqrs <name...>, '创建cqrs结构')
 */

/**
 * 别名
 */
const generate = program.command('generate').alias('g')

/**
 * cqrs
 */
generate
  .command('cqrs')
  .alias('cq')
  .argument('<name>', '名字')
  .option(
    "-p, --parent <name...>",
    "如 user create，将转换为user/modules/create"
  )
  .action(async (name, options) => {
    const { parent } = options
    // 执行命令
    generateCqrs(name, getParentPath(parent, name))
  });


generate
  .command('centity')
  .alias('ct')
  .argument('<name>', '实体名字')
  .argument('[path]', "实体路径")
  .action(async (name, path) => {
    const nameDx = name.charAt(0).toUpperCase() + name.slice(1)
    const entitiesPath = join(process.cwd(), './src/', path, "/entities/")
    const providers = join(process.cwd(), "./src/", path, "/providers/")
    exitsFolder(entitiesPath)
    exitsFolder(providers)

    // 实体
    const entityTpl = `
import { Entity, PrimaryGeneratedColumn } from 'typeorm'

@Entity({ name: '${name}' })
export class ${nameDx}Entity {
  @PrimaryGeneratedColumn()
  id?: number
}
`;
    // 订阅模板
    const entitySubcriberTpl = `
import {
  DataSource,
  EntitySubscriberInterface,
  EventSubscriber,
  InsertEvent,
} from 'typeorm';
import { ${nameDx}Entity } from './${name}.mysql.entity';

@EventSubscriber()
export class UserSubscriber implements EntitySubscriberInterface<${nameDx}Entity> {
  constructor(dataSource: DataSource) {
    dataSource.subscribers.push(this);
  }

  listenTo() {
    return ${nameDx}Entity;
  }

  beforeInsert(event: InsertEvent<${nameDx}Entity>) {
    console.log('执行前触发: ', event.entity);
  }
}
`

    // 自定义源注入模板
    const provdersTpl = `
import { ${nameDx}Entity } from '../entities/${name}.mysql.entity'
import {${nameDx}Subscriber} from '../entities/${name}.mysql.entity.subscriber'
export const ${nameDx}Providers = [
  {
    provide: '${name.toUpperCase()}_REPOSITORY',
    useFactory: async (AppDataSource) => await AppDataSource.getRepository( ${nameDx}Entity),
    inject: ['MYSQL_DATA_SOURCE']
  },
  {
    provide:'${name.toUpperCase()}_SUBSCRIBER',
    useFactory: (dataSource: DataSource) => new ${nameDx}Subscriber(dataSource),
    inject: ["MYSQL_DATA_SOURCE"] // 具名注入数据源
  }
]
`


    fs.writeFileSync(entitiesPath + name + ".mysql.entity.ts", entityTpl, 'utf8')
    fs.writeFileSync(entitiesPath + name + ".mysql.entity.subscriber.ts", entitySubcriberTpl, 'utf8')
    fs.writeFileSync(providers + name + ".provider.ts", provdersTpl, 'utf8')
  })

function generateCqrs(name, path) {
  if (name.indexOf('-') == -1) {
    console.log("请输入格式类似于 create-user")
    return
  }
  /**
   * name 转换成路径
   */
  const pathNameArr = name.split('-')
  const nameA = pathNameArr[0].charAt(0).toUpperCase() + pathNameArr[0].slice(1)
  const nameB = pathNameArr[1].charAt(0).toUpperCase() + pathNameArr[1].slice(1)
  const ClassName = nameA + nameB
  /**
   * 生成command文件
   */
  const commandTpl = `
export interface ${ClassName}Params { }
export class ${ClassName}Command {
  constructor(public readonly params: ${ClassName}Params) { }
}
`

  /**
   * 生成command.handler
   */
  const handlerTpl = `
import { CommandHandler, ICommandHandler, EventBus } from '@nestjs/cqrs'
import { ${ClassName}Command } from './${name}.command'
import { ${ClassName}Event } from '../events/${name}.event'
@CommandHandler(${ClassName}Command)
export class ${ClassName}CommandHandler implements ICommandHandler<${ClassName}Command> {
  constructor(private readonly enventBus: EventBus) { }
  async execute(command: ${ClassName}Command) {
    const { } = command.params
    this.enventBus.publish(new ${ClassName}Event(command.params))
  }
}
`

  /**
   * 生成event
   */
  const eventTpl = `
export interface ${ClassName}EventParams { }
export class ${ClassName}Event {
  constructor(public readonly params: ${ClassName}EventParams) { }
}
`

  /**
   * 生成Controller
   */
  const controllerTpl = `
import { Body, Controller, Post } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';
import { ${ClassName}Dto } from '../dtos/${name}.dto';
import { ${ClassName}Command } from '../commands/${name}.command'
@Controller('')
export class ${ClassName}Controller {
  constructor(private readonly commandBus: CommandBus) { }
  @Post()
  createUser(@Body() body: ${ClassName}Dto) {
    this.commandBus.execute(
      new ${ClassName}Command(body)
    );
  }
}
`

  /**
   * 生成dto
   */
  const dtoTpl = `
export class ${ClassName}Dto {}
`


  /**
   * 生成module
   */
  const moduleTpl = `
import { Module,forwardRef } from '@nestjs/common';
import { ${ClassName}Controller } from './controllers/${name}.controller'
import { ${ClassName}CommandHandler } from './commands/${name}.command.handler'
import { ${ClassName}Event } from './events/${name}.event'
import { ${ClassName}Module } from '@/${name}/${name}.module'

const CommandHandlers = [${ClassName}CommandHandler]
const eventHandlers = [${ClassName}Event]
@Module({
   imports: [
    forwardRef(() => ${ClassName}Module),
  ],
  controllers: [${ClassName}Controller],
  providers: [
    ...CommandHandlers,
    ...eventHandlers,
  ]
})
export class ${ClassName}Module { }

`

  /**
   * 创建文件夹
   */
  exitsFolder(join(process.cwd(), "./src/", path, "/commands/"))
  exitsFolder(join(process.cwd(), "./src/", path, "/events/"))
  exitsFolder(join(process.cwd(), "./src/", path, "/dtos/"))
  exitsFolder(join(process.cwd(), "./src/", path, "/controllers/"))

  const commandPath = join(process.cwd(), "./src/" + path + "/commands/" + name)
  const eventPath = join(process.cwd(), "./src/" + path + "/events/" + name)
  const controllerPath = join(process.cwd(), "./src/" + path + "/controllers/" + name)
  const dtoPath = join(process.cwd(), "./src/" + path + "/dtos/" + name)
  fs.writeFileSync(commandPath + ".command.ts", commandTpl, 'utf8')
  fs.writeFileSync(commandPath + ".command.handler.ts", handlerTpl, 'utf8')
  fs.writeFileSync(eventPath + ".event.ts", eventTpl, 'utf8')
  fs.writeFileSync(controllerPath + ".controller.ts", controllerTpl, 'utf8')
  fs.writeFileSync(dtoPath + ".dto.ts", dtoTpl, 'utf8')
  fs.writeFileSync(join(process.cwd(), "./src/" + path + "/" + name + ".module.ts"), moduleTpl, 'utf8')
}


/**
 * 获取父级目录
 */
function getParentPath(parent: Array<string>, name: string = '') {
  let parentPath = '';

  if (parent) {
    parentPath =
      parent.length > 1
        ? join(parent[0], 'modules', parent[1])
        : join(parent[0], 'modules');
  } else {
    parentPath = name;
  }

  return parentPath;
}

/**
 * 创建文件夹
 */

function exitsFolder(dirPath) {
  const paths = dirPath.split(sep)
  console.log(paths)
  for (let i = 1; i < paths.length; i++) {
    const newPath = paths.slice(0, i).join('/');
    try {
      //是否能访问到这个文件，如果能访问到，说明这个文件已经存在，进入循环的下一步。
      //accessSync的第二个参数就是用来判断该文件是否能被读取
      fs.accessSync(newPath, fs.constants.R_OK);
    } catch (e) {
      fs.mkdirSync(newPath);
    }
  }
}

// 解析
program.parse()